# coding=utf-8
"""
Copyright (c) Huawei Technologies Co., Ltd. 2020-2028. All rights reserved.
Description:
FileName：start.py
Author: h00568282/huangbin 
Create Date: 2025/9/18 16:52
Notes:

"""

import os
import re
import time
import subprocess
from typing import List


from sys_intd.constant import PROJECT_MCP_CENTER_PATH as MCP_CENTER_PATH, AUTO_PORT
from sys_intd.exceptions import MCPServerStartError
from sys_intd.manager import McpServerManager
from sys_intd.schema import MCPCenterServerConfig, STDIOServerConfig
import toml
import psutil
from pathlib import Path
import rich.console as console
console = console.Console()


def start_server_with_port_retry(server_name, initial_port=AUTO_PORT) -> List[str]:
    """
    启动服务器，当端口占用时自动修改配置文件并递增端口重试
    返回:
        启动成功的进程对象和使用的端口号
    """
    current_port = initial_port
    output_lines = []
    mcp_server_manager = McpServerManager()
    count = 100
    # 确保配置文件存在 /usr/lib/euler-copilot-framework/mcp_center/config/private/ls/config.toml
    config_file = (
            Path(mcp_server_manager.mcp_center_path)
            / "config"
            / "private"
            / server_name
            / "config.toml"
    )
    if not config_file.exists():
        raise FileNotFoundError(f"Configuration file {config_file} not found")
    server_config = mcp_server_manager.get_server(server_name)
    while True:
        if count < 0:
            break
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                content = f.read().strip()
            if not content:
                config = {}
            else:
                config = toml.loads(content)
            config['port'] = current_port
            with open(config_file, 'w', encoding='utf-8') as f:
                toml.dump(config, f)

            console.print(f"[bold]Trying to start '{server_name}' on port {current_port}")

            # 构建启动命令
            python_cmd = [server_config.command, *server_config.args]
            process = subprocess.Popen(python_cmd, cwd=MCP_CENTER_PATH,
                                       stderr=subprocess.PIPE, text=True)

            time.sleep(2)
            if process.poll() is None:
                update_port(mcp_server_manager, server_name, server_config)
                output_lines.append(f"[bold cyan]Uvicorn running on {server_config.url}.")
                console.print(f"[bold cyan]Uvicorn running on {server_config.url}.")
                output_lines.append(f'''\n[bold]'{server_name}' start up ok, process id: {process.pid}.\n''')

                break
            else:
                exit_code = process.poll()
                # 读取错误输出
                error_output = process.stderr.read()

                # 检查是否是端口占用错误（适用于Uvicorn）
                port_occupied = "address already in use" in error_output.lower() or \
                                "端口已被占用" in error_output

                if port_occupied:
                    output_lines.append("[bold red]Possible reason: The port is already in use by another process.\n")
                    output_lines.append("[bold yellow]try another port...\n")
                    current_port += 1

                    continue
                else:
                    raise MCPServerStartError(
                        f'''\n[bold cyan]'{server_name}' start fail, process id: {process.pid}, exit_code: {exit_code}.\n''')

        except Exception as e:
            output_lines.append(f"[red]Error: {str(e)}")
            raise
    return output_lines


def is_activate(server_name: str = "cache-miss-audit") -> bool:
    """
    查看MCP服务运行状态

    Args:
        server_name: 服务名称

    Returns:
        状态信息列表
    """
    mcp_server_manager = McpServerManager()
    server_config = mcp_server_manager.get_server(server_name)
    python_cmd: List = [server_config.command, *server_config.args]
    if len(python_cmd) < 2:
        return False
    server_path = python_cmd[1]
    for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'exe']):
        try:
            cmdline = proc.info['cmdline']  # 进程启动命令行，是一个列表，如 ['python3', 'xxx.py']
            process_name = proc.info['name']
            if not cmdline or len(cmdline) < 2:
                continue
            if process_name != 'python3' and process_name != 'python':
                continue
            if cmdline[0] != python_cmd[0]:
                continue
            launched_script = cmdline[1] if len(cmdline) > 1 else ""
            if launched_script.endswith(server_path):
                return True
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            continue
    return False


def update_port(mcp_server_manager, server_name, server_config):
    port = mcp_server_manager.get_mcp_port(server_name)
    console.print(f"port {port} is open")
    if port is not None:
        server_config.url = re.sub(r'(:\d+)/sse', f':{port}/sse', server_config.url)
        console.print(server_config)
        mcp_server_manager.update_server(server_config)
    else:
        # 获取原始配置文件更新
        server_config.url = mcp_server_manager.get_default_mcp_port(server_name)
        console.print(str(server_config.url))
        mcp_server_manager.update_server(server_config)


def start(server_name: str = "cache-miss-audit", http: bool = False, sse: bool = False, port: str = "0.0.0.0",
          host: int = 5500) -> List[str]:
    output_lines = []
    mcp_server_manager = McpServerManager()

    if not os.path.exists(MCP_CENTER_PATH):
        raise MCPServerStartError(
            f"Install mcp center before, project https://gitee.com/zxstty/mcp_center into {MCP_CENTER_PATH}")

    os.environ["PYTHONPATH"] = MCP_CENTER_PATH
    output_lines.append(f"\n[bold cyan]Prepare start server '{server_name}'")
    server_metadata = mcp_server_manager.get_mcp_metadata(server_name)
    real_name = mcp_server_manager.get_mcp_server_name(server_name)
    if not server_metadata or real_name != f"{server_name}_mcp":
        output_lines.append(f"[yellow]未找到 名称： {server_name} 的mcp服务[/]")
        return output_lines

    if mcp_server_manager.server_exists(server_name):
        output_lines.append(f'''[bold]'{server_name}' has installed.''')

        server_config = mcp_server_manager.get_server(server_name)
        if isinstance(server_config, MCPCenterServerConfig):
            if is_activate(server_name) is True:
                output_lines.append(f"[bold cyan]Uvicorn is already running on {server_config.url}.")
            else:
                python_cmd = [server_config.command, *server_config.args]
                output_lines.append(f"[bold]Start command: {python_cmd}")
                process = subprocess.Popen(python_cmd, cwd=MCP_CENTER_PATH,
                                           stderr=subprocess.PIPE, text=True)

                time.sleep(2)
                if process.poll() is None:
                    update_port(mcp_server_manager, server_name, server_config)
                    output_lines.append(f"[bold cyan]Uvicorn running on {server_config.url}.")
                    output_lines.append(f'''\n[bold]'{server_name}' start up ok, process id: {process.pid}.\n''')
                else:
                    exit_code = process.poll()
                    # 读取错误输出
                    error_output = process.stderr.read()

                    # 检查是否是端口占用错误（适用于Uvicorn）
                    port_occupied = "address already in use" in error_output.lower() or \
                                    "端口已被占用" in error_output
                    console.print(error_output)
                    if port_occupied:
                        output_lines.append(
                            "[bold yellow]Possible reason: The port is already in use by another process.\n")
                        output_lines.append("[bold yellow]try another port...\n")
                        output_lines.extend(start_server_with_port_retry(server_name))
                    else:
                        raise MCPServerStartError(
                            f'''\n[bold cyan]'{server_name}' start fail, process id: {process.pid}, exit_code: {exit_code}.\n''')
        elif isinstance(server_config, STDIOServerConfig):
            # todo for other mcp type
            output_lines.append(
                f'''\n[bold cyan]'{server_name}' start fail, other type mcp would match later.\n''')
            pass

    return output_lines


def main():
    start()


if __name__ == "__main__":
    main()
